# -*- coding: UTF-8 -*-
'''
This module implements the harvesting of links and fallback metadata from the tvshack.cc website 

Created on Jan 24, 2010

@author: pguedes
'''
import logging
from metadata.metadata import Metadata
from utils.pluginsupport import PluginMovieItem, PluginHandler, encode
import linkresolvers
import metadata.metadata as metadatautils
import utils.htmlutils as http
import utils.settings as settings
import xbmc,xbmcgui, re,urllib #@UnresolvedImport

log = logging.getLogger("tvshack")

def _getMode(url):
  '''Get the execution mode for this plugin
  The execution mode is used by the plugin to track the type of url being used
  @param url: the url to get the execution mode for'''
  mode = LIST_EPISODES
  if url.find('/movies/') >= 0:
    mode = PLAY_ITEM
  elif url.find('/documentaries/') >= 0 or url.find('/comedy/') >= 0:
    mode = LIST_PLAYABLE_ITEMS
  return mode
  
def _harvestCoverArt(html):
  '''Read the cover from a TVShack tvshow/movie/documentary page
  @param html: the page's html to parse from
  @return: the cover from tvshack or "" if not found'''
  thumb = re.compile('<img src="(.+?)" alt="Poster" />').findall(html)
  log.debug("Found thumb: %s" % thumb)
  if len(thumb) == 1:
    return thumb[0]
  return ""

def _harvestSummary(html):
  '''Read the summary from a TVShack tvshow/movie/documentary page
  @param html: the page's html to parse from
  @return: the summary from tvshack or "" if not found'''
  summary = re.compile('<h3>Summary</h3>.+?<div>(.+?)</div>', re.DOTALL).findall(html)
  log.debug("Found summary: %s" % summary)
  if len(summary) == 1:
    return summary[0]
  return ""

def _loadInfo(url, name, edit=False):
  '''Force a refresh of the metadata information for an item.
  @param url: url of the title being refreshed
  @param name: the name of the title being refreshed (used for querying)
  @return: true if the metadata was refreshed with online db data, false otherwise'''
  log.info("Loading info for url '%s' of type '%s'" % (url, type))
  search = None
  if edit:
    keyb = xbmc.Keyboard(name, 'Edit title to search')
    keyb.doModal()
    if (keyb.isConfirmed()):
      search = keyb.getText()
  else:
    search = name
  if search:
    meta = metadatautils.get(url, queryOnlineDb=True, bypassCache=True, title=search)
    if not meta.hasCode() and edit == False:
      return _loadInfo(url, search, True)
    return meta.hasCode()
                
def _selectSource(sources, forceSourceSelection):
  if not sources:
    return - 1
  elif len(sources) == 1:
    return 0
  else:
    if settings.isSet("autoplay-preferred-source") and not forceSourceSelection:
      autoselect = settings.get("preferred-source")
      for index in range(len(sources)):
        if sources[index] == autoselect:
          return index
    else: 
      return xbmcgui.Dialog().select('Select source', sources)
                  

def categoryContentType(params):
  url = params['url']
  if url.find('/tv/') >= 0:
    return 'tvshows'
  elif url.find('/music') >= 0:
    return 'artists'
  else:
    return 'movies'


# The list of modes of this plugin
LIST_CATEGORIES, PLAY_ITEM, LIST_PLAYABLE_ITEMS, LIST_EPISODES, SEARCH, RSS = 1, 2, 3, 4, 6, 7
class TVShackHandler(PluginHandler):
  def isAction(self, params):
    return params.has_key('action') and params['action'] != 'selectSource'
  
  @staticmethod
  @PluginHandler.default()
  def listCategories():
    '''List the root categories for this plugin
    @return: a list of PluginMovieItems with the root categories'''
    yield PluginMovieItem("Tv", 'http://tvshack.cc/tv', mode=LIST_CATEGORIES, listSize=5)
    yield PluginMovieItem("Movies", 'http://tvshack.cc/movies', mode=LIST_CATEGORIES, listSize=5)
    yield PluginMovieItem("Anime", 'http://tvshack.cc/anime', mode=LIST_CATEGORIES, listSize=5)
    yield PluginMovieItem("Music", 'http://tvshack.cc/music', mode=LIST_CATEGORIES, listSize=5)
    yield PluginMovieItem("Documentaries", 'http://tvshack.cc/documentaries', mode=LIST_CATEGORIES, listSize=5)
  
  @staticmethod
  @PluginHandler.mode('1', contentType=categoryContentType)
  def listCategory(url, name):
    '''List all the TVShows/Movies/... available
    @param url: url of the title being refreshed
    @param name: the name of the title being refreshed (used for querying)
    @return: a list of PluginMovieItems'''
    page = http.get(url, cleanup=True)
    #log.debug(page)
    match = re.compile('<li><a href="(.+?)">(.+?)<span').findall(page)
    #log.debug("Results: %s"%str(match))
    for url, name in match:
      # --------------------------------------------------------------
      # For some strange reason, when this code is not here, the loop will fail
      # There seems to be something in the url or name that breaks it.
      try:
        if name:
          log.debug("name size is %s" % str(len(name)))
          log.debug("name is %s" % str(name))
        else:
          log.debug("no name!!!!")
      except:
        log.exception("Fail!")
  
      tags = None
      if name.find('<font class="new-updated">Updated!</font>') > 0:
        name = name.replace('<font class="new-updated">Updated!</font>', '')
        tags = '[ Updated! ]'
      elif name.find('<font class="new-new">New!</font>') > 0:
        name = name.replace('<font class="new-new">New!</font>', '')
        tags = '[ New! ]'
          
      metadata = metadatautils.get(url, title=name)
      # movies and documentary categories are lists of playable items
      playable = url.find('/movies/') >= 0 or url.find('/documentaries/') >= 0 
      yield PluginMovieItem(name, url, _getMode(url), metadata, tags, playable, listSize=len(match))

  @staticmethod
  @PluginHandler.mode('4', contentType='episodes')
  def listEpisodes(url, name):
    '''List the episodes for a TV Show
    @param url: the tvshack url to load episodes from
    @param name: the tvshow being listed
    @return: a list of PluginMovieItem with the episode items'''
    html = http.get(url, cleanup=True)
    fallbackcover = _harvestCoverArt(html)
    fallbackplot = _harvestSummary(html)
    tvshowMetadata = metadatautils.get(url, fallbackplot, fallbackcover, True, title=name, tvshow=True, persistUpdates=False)
    log.info("tvshow details: %s" % str(tvshowMetadata))
    fallbackMetadata = Metadata(name, fallbackplot, fallbackcover).tvshow(name)

    episodeLinks = re.compile('<li><a href="([^"]+)">([^<]+)</a><a href=""><span>[^<]+?</span></a></li>').findall(html)
    itemCount = len(episodeLinks)
    item = None
    items = []
    for url, episode in episodeLinks:
      try:
        try:
          episodeMetadata = metadatautils.get(url, fallbackplot, fallbackcover, True, title=episode, tvshow=tvshowMetadata, persistUpdates=False)
          #log.debug("Episode meta: %s" % str(episodeMetadata))
          title = episodeMetadata.getSeasonedTitle()
          item = PluginMovieItem(title, url, 2, episodeMetadata, listSize=itemCount)
          log.debug("Added item with title: %s" % title)
        except:
          log.exception("Failed to load episode data: %s" % str(episode))
          fallbackMetadata.title(episode)
          item = PluginMovieItem(episode, url, 2, fallbackMetadata, playable=True, listSize=itemCount)
      finally:
        items.append(item)
    metadatautils.flushCache(url)
    return items

  @staticmethod
  @PluginHandler.mode('2', playable=True)
  def resolveFiles(url, name, forceSourceSelection=False):
    '''Resolve the files for a movie/episode/... item
  
    Resolving a file is split into three phases:
    1-Select file source (megavideo, veoh, tudou, youku, ...)
    2-Load parts (if item is split for the selected source)
    3-Resolve file links for the parts from the previous step
    
    If an item has only one available source, then that source is auto-selected, otherwise
    the user is shown a XBMC dialog to select the source. If the 'autoplay-preferred-source'
    setting is enabled, the first available source that matches the 'preferred-source' setting
    will be auto-selected.
    
    @param url: the url to resolve files for
    @param name: the name of the playable item being resolved
    @param forceSourceSelection: if the user should be forced to select the source (default False)
    @return: a list of urls of the files to play'''
    log.debug("Listing sources: %s" % url)
    html = http.get(url, cleanup=True)
    alt = re.compile('<h3>Alternate links</h3>.(.+?)</ul>', re.DOTALL).findall(html)
    alternateLinks = re.compile('<li><a href="(.+?)"><img src=".+?logos\/(.+?)\.gif" />').findall(str(alt))
    # check if the source has only one part and if so don't show popup...
    sources = {}
    filterUnsupported = settings.isSet("filter-unsupported-sources")
    for url, source in alternateLinks:
      if linkresolvers.isSupported(source) or not filterUnsupported:
        if not sources.has_key(source):
          sources[source] = []
        sources[source].append(url)
    
    log.debug("Found alternate sources: %r" % sources)
    # if we need to resolve manually, we need individual links as choices
    if forceSourceSelection:
      flattenedSources = {}
      for source, urls in sources.iteritems():
        for url in urls:
          flattenedSources["%s#%d" % (source, urls.index(url))] = [url]
      sources = flattenedSources
      
    sourceNames = sources.keys()
    selected = _selectSource(sourceNames, forceSourceSelection)
    if selected >= 0:
      from linkresolvers import Source
      selSource = sourceNames[selected]
      metadata = metadatautils.get(sources[selSource][0], title=name)
      source = Source(sources[selSource], selSource, metadata)
      return source.resolve()
  
  @staticmethod
  def RSS(url):
    '''Load RSS feed with most recent updates
    @deprecated: this is currently not used, because I don't!'''
    link = http.get(url)
    rss = re.compile('<title>(.+?)</title>\n\t\t<link>(.+?)</link>').findall(link)
    for name, ref in rss:
      mode = 3
      if url.find('movies.xml') > 0:
        mode = 2
      yield PluginMovieItem(name, ref, mode, listSize=len(rss)) 
  
  @staticmethod
  def doInteractiveSearch():
    '''Do an interactive search of tvshack.cc's files
    @deprecated: this is currently not used, because I don't!'''
    items = []
    keyb = xbmc.Keyboard('', 'Search TV Shack')
    keyb.doModal()
    if (keyb.isConfirmed()):
      search = keyb.getText()
      encode = urllib.quote(search)
      html = http.get('http://tvshack.cc/search/' + encode, cleanup=True)
      match = re.compile('<li><a href="(.+?)">.+?<strong>(.+?)</strong></a>').findall(html)
      for url, name in match:
        items.append(PluginMovieItem(name, url, _getMode(url)))
    return items
   
  @PluginHandler.action('loadInfo')
  def loadInfoAction(self, url=None, name=None, mode=None):
    if _loadInfo(url, name):
      xbmc.executebuiltin("Container.Refresh")
    else:
      xbmcgui.Dialog().ok("Load Info...", "Failed!", "Could not load metadata for item: '%s'" % name)

def _getListingUrlAndMode(url, mode):
  """
  Get the listing url and mode from another url and mode.
  This supports movies, tvshows and episode urls and will return the corresponding listing
  E.G: tvshack.cc/tv/Till_Death -> tvshack.cc/tv
  """
  listMode = LIST_CATEGORIES
  if mode == PLAY_ITEM and url.find("/tv/") >= 0:
    listMode = LIST_EPISODES
  return '/'.join(url.split('/')[:-2]), listMode
